#include "AbstractFX.h"

AbstractFX::AbstractFX(ID3D11Device* device, const std::wstring& filename)
{
	//Init pointers to NULL.
	inputLayout = NULL;
	activeTechniqueData = NULL;
	effect = NULL;

	//This essentially sets our effect pointer - standard method copy and
	//pasted from the book! Note, however, you MUST! be compiling your shaders/
	//effect files at BUILD TIME : 
	//Right Click your .fx file -- Properties -- General -- Set Item Type to "Custom
	//Build Tool" -- Custom Build Tool drop down box -- General.
	//
	//For Release : Command Line == fxc /T fx_5_0 /Fo "%(RelativeDir)\%(Filename).fxo" "%(FullPath)"
	//              Description  == fxc compile for release: %(FullPath)
	//				Outputs      ==  %(RelativeDir)\%(Filename).fxo
	//
	//For Debug :   Command Line == fxc /Fc /Od /T fx_5_0 /Fo "%(RelativeDir)\%(Filename).fxo" "%(FullPath)"
	//              Description  == fxc compile for debug: %(FullPath)
	//				Outputs      == %(RelativeDir)\%(Filename).fxo
	//
	//
	//Go ahead and load effect and set effect pointer. We read the compiled shader 
	//in to a vector from memory. We can then create the effect using the read in
	//data.
	std::ifstream fin(filename, std::ios::binary);

	fin.seekg(0, std::ios_base::end);
	int size = (int)fin.tellg();
	fin.seekg(0, std::ios_base::beg);
	std::vector<char> compiledShader(size);
	fin.read(&compiledShader[0], size);
	fin.close();
	
	HR(D3DX11CreateEffectFromMemory(&compiledShader[0], size, 
		0, device, &effect));

	//Obtain effect description - we can use this to obtain how many
	//techniques in total exist in this effect
	effect->GetDesc(&effectDesc);

	//Init technique data
	//Obtain the number of techniques for this effect file.
	unsigned noOfTechniques = effectDesc.Techniques;
	//Resise our vector.
	techniqueArray.resize(noOfTechniques);

	//Set struct for each of the techniques in this effect file.
	for (unsigned i = 0; i < noOfTechniques; i++)
	{
		//Alloc new data
		techniqueArray[i] = new TechniqueData();

		//TECHNIQUES
		//First, set technique pointer.
		techniqueArray[i]->tech = effect->GetTechniqueByIndex(i);
		//Set technique description.
		techniqueArray[i]->tech->GetDesc(&techniqueArray[i]->techDesc);
		//Set the technique name
		techniqueArray[i]->techniqueName = techniqueArray[i]->techDesc.Name;

		//PASSES
		//Set number of passes.
		techniqueArray[i]->noOfPasses = techniqueArray[i]->techDesc.Passes;
		//Create array of pass D3DX11_PASS_DESC - one per pass. 
		//Dynamically create our array first
		techniqueArray[i]->passDescArray = new D3DX11_PASS_DESC[techniqueArray[i]->noOfPasses];
		//Enter the loop. 
		for (unsigned j = 0; j < techniqueArray[i]->noOfPasses; j++)
			techniqueArray[i]->tech->GetPassByIndex(j)->GetDesc(&techniqueArray[i]->passDescArray[j]);

	}//End for (i)

	//Init currently active technique data (first technique).
	activeTechniqueData = techniqueArray[0];
}

AbstractFX::~AbstractFX()
{
	//Release the effect
	ReleaseCOM(effect);
	ReleaseCOM(inputLayout);

	//itterate through our TechniqueData* vector and clear up. 
	std::vector<TechniqueData*>::iterator it;
	it = techniqueArray.begin();
	while (it != techniqueArray.end())
	{
		//Delete data
		delete (*it);
		//Increment itterator.
		it++;
	}

	//Clear verctor
	techniqueArray.clear();

	//Reset active tech pointer
	activeTechniqueData = NULL;
}

void AbstractFX::SetActiveTechniqueByName(LPCSTR techniqueName)
{
	//bool indicating if a match has been found. If this is false
	//at the end of the loop, print an error message.
	bool matchFound = false;

	//itterate through our TechniqueData* vector find one that matches. 
	std::vector<TechniqueData*>::iterator it;
	it = techniqueArray.begin();
	while (it != techniqueArray.end())
	{
		std::string vectorTechName = (*it)->techniqueName;
		std::string techNameToTest = techniqueName;
		if (strcmp(vectorTechName.c_str(), techNameToTest.c_str()) == 0)
		{
			//Match found - set bool before setting the activeTechniqueData
			//pointer
			matchFound = true;
			activeTechniqueData = (*it);
			//Jump to the end of the vector. 
			it = techniqueArray.end();
		}
		else
			//Increment itterator.
			it++;
	}

	//Check if a match was found
	if (!matchFound)
	{
		//Output error 
		OutputDebugStringW(L"***SHADER ERROR**** Can not set active FX (STRING)\n");
		OutputDebugStringA(techniqueName);
		while (1){} //Infinite loop. 
	}
}


void AbstractFX::SetActiveTechniqueByIndex(unsigned index)
{
	if (index > techniqueArray.size())
	{
		//Print error if the index is too big.
		OutputDebugStringW(L"****SHADER ERROR*** Can not set active FX (INDEX)\n");
		while(1){}
	}
	else
		activeTechniqueData = techniqueArray[index];
}

void AbstractFX::ApplyActiveTechniqueForPass(unsigned p, ID3D11DeviceContext* context)
{
	if (p > activeTechniqueData->noOfPasses)
	{
		OutputDebugStringW(L"***SHADER ERROR**** Can not apply: Pass index (p) > " 
						   L"techniques number of passes\n");
		while(1){}
	}
	else
		activeTechniqueData->tech->GetPassByIndex(p)->Apply(0, context);
}


ID3DX11EffectPass* AbstractFX::ReturnActiveTechniquePassPointer(unsigned index)
{
	if (index > activeTechniqueData->noOfPasses)
	{
		//If the requested data does not exisit (Ie, we have requested
		//the Pass Pointer for pass index 1 when only 1 (index 0)
		//pass exists, print error and enter infinite loop
		OutputDebugStringW(L"****SHADER ERROR**** Can not return Pass Pointer - " 
					       L"Index > techniques number of passes\n");
		while(1){}
	}

	//If the above statement is true, we will enter an infinite loop
	//indicating an error, else return the requested data.
	return activeTechniqueData->tech->GetPassByIndex(index);
}

//************************
//****Helper Functions****
//************************

void AbstractFX::CreateInputLayout(ID3D11Device* device, 
									   const D3D11_INPUT_ELEMENT_DESC* inputLayoutDesc,
									   unsigned noOfElements)
{
	//Get pass description
	D3DX11_PASS_DESC passDesc;
	activeTechniqueData->tech->GetPassByIndex(0)->GetDesc(&passDesc);

	//Create input layout. 
	HR(device->CreateInputLayout(inputLayoutDesc, noOfElements, passDesc.pIAInputSignature, 
		passDesc.IAInputSignatureSize, &inputLayout));
}